Skip to content

feat(website): lighter docs (GIFs → WebP) + contributors page#44

Merged
rubenmarcus merged 2 commits intomainfrom
feat/lighter-docs-and-contributors-page
May 4, 2026
Merged

feat(website): lighter docs (GIFs → WebP) + contributors page#44
rubenmarcus merged 2 commits intomainfrom
feat/lighter-docs-and-contributors-page

Conversation

@rubenmarcus
Copy link
Copy Markdown
Member

Summary

Two improvements to the docs site at website/:

1. Replace heavy GIFs with WebP still frames

The introduction page (introduction.mdx) and widget feature page (widget.mdx) referenced 4 animated GIFs totaling 4.4 MB, including a 3.5 MB example.gif that almost certainly was the LCP element on every first-time visit.

Asset Before After Reduction
example.gif 3.5 MB example.webp 9.9 KB -99.7%
widget-default.gif 517 KB widget-default.webp 7.8 KB -98.5%
widget-small.gif 223 KB widget-small.webp 7.6 KB -96.6%
widget-icon.gif 146 KB widget-icon.webp 4.8 KB -96.7%
Total public/ ~4.5 MB ~30 KB -99.3%

Frames extracted via ffmpeg -vf "select=eq(n,N)" at representative mid-loop indices for each GIF. If a frame ends up looking off, it's a one-line tweak to the index — easy to swap out later.

2. Add /contributors page

New Astro route at website/src/pages/contributors.astro. Fetches https://api.github.com/repos/multivmlabs/aeo.js/contributors at build time, filters bots (type === 'User'), sorts by contribution count, and renders a static card grid.

  • No client-side JS, no runtime API calls — fully static.
  • Bot accounts (dependabot, claude, etc.) excluded automatically.
  • On fetch failure (rate-limit, network), falls back to a link to the GitHub contributors page rather than failing the build.
  • Wrapped in <StarlightPage> so it inherits site chrome (header, footer, theme).
  • Linked from Header.astro between "Docs" and "Checker".

Currently lists 4 contributors:

Test plan

  • bun run build — site builds, 19 pages, no errors
  • dist/contributors/index.html contains all 4 contributors
  • dist/getting-started/introduction/index.html references /example.webp (not .gif)
  • du -sh website/public/ — 208K (down from ~4.5 MB)
  • Manual: visit homepage → verify intro loads fast and the WebP frame looks reasonable
  • Manual: visit /contributors/ → verify grid renders, header link works, all avatars load
  • Manual: click a contributor card → opens GitHub profile in new tab

Notes

  • Hero typewriter and Terminal animations on the homepage are kept (lightweight inline JS, adds character).
  • Rate-limit risk on the GitHub API call is bounded by the build-time fallback — a failed fetch produces a graceful "see contributors on GitHub" link, never breaks the build.
  • If we ever hit rate limits in CI, plumb a GITHUB_TOKEN env var through and add an Authorization header.

🤖 Generated with Claude Code

Two improvements to the docs site:

1. **Drop 4.4 MB of animated GIFs.** The introduction and widget pages
   referenced four animated GIFs totaling 4.4 MB (example.gif alone was
   3.5 MB). Replaced each with a representative WebP still frame
   extracted via ffmpeg. The combined size is now ~30 KB — a ~99%
   reduction in public/ asset weight, with most of that being LCP
   savings on the introduction page.

2. **Add /contributors page.** New top-level Astro route at
   website/src/pages/contributors.astro that fetches the GitHub
   contributors list at build time and renders a static card grid.
   No client-side JS, no runtime API calls. Bots are filtered out via
   `type === 'User'`. On fetch failure (rate-limit, network), a
   fallback message links to the GitHub contributors page rather
   than failing the build. Linked from Header.astro nav between
   "Docs" and "Checker".

Local build: 19 pages, contributors page lists current 4 humans.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@vercel
Copy link
Copy Markdown

vercel Bot commented May 4, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
aeo-js Ready Ready Preview, Comment May 4, 2026 11:56am

Request Review

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 4, 2026

Docs Preview

Preview URL: https://feat-lighter-docs-and-contri.aeojs.pages.dev

This preview was deployed from the latest commit on this PR.

@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented May 4, 2026

Greptile Summary

Replaces four heavy GIFs (~4.5 MB) with lightweight WebP still frames (~30 KB total) and adds a new /contributors page plus a homepage strip, both fed by a build-time GitHub API fetch with a graceful fallback. The GIF-to-WebP swap and the header link are clean; the main concern is that HomepageContributors.astro and contributors.astro each independently call the same GitHub API endpoint, doubling rate-limit consumption per build, and both duplicate the GhContributor type — both are worth cleaning up before the build pipeline gets heavier.

Confidence Score: 5/5

Safe to merge — all findings are P2 style/efficiency concerns, no runtime or logic bugs.

No P0 or P1 issues found. The GIF-to-WebP migration is correct and the contributors pages build successfully. The only concerns are duplicate API calls and type duplication, both P2.

website/src/components/HomepageContributors.astro and website/src/pages/contributors.astro — both should share a single fetch helper to avoid the duplicate API call pattern.

Important Files Changed

Filename Overview
website/src/pages/contributors.astro New contributors page; fetches GitHub API at build time with graceful fallback. Has the same duplicate-fetch and fragile avatar URL issues as HomepageContributors.astro.
website/src/components/HomepageContributors.astro New homepage strip component; duplicates the GhContributor type and GitHub API fetch from contributors.astro, doubling rate-limit consumption per build.
website/src/components/Header.astro Adds a "Contributors" nav link between "Docs" and "Checker" — straightforward one-line change.
website/src/content/docs/getting-started/introduction.mdx Swaps example.gif reference for example.webp — clean, no issues.
website/src/content/docs/features/widget.mdx Replaces three widget GIF references with WebP equivalents — clean, no issues.
website/src/content/docs/index.mdx Imports and renders HomepageContributors at the bottom of the homepage — no issues with the integration itself.

Sequence Diagram

sequenceDiagram
    participant Build as Astro Build
    participant HP as index.mdx (HomepageContributors)
    participant CP as contributors.astro
    participant GH as GitHub API

    Build->>HP: render homepage
    HP->>GH: GET /repos/multivmlabs/aeo.js/contributors
    GH-->>HP: JSON (or error → silent skip)
    HP->>HP: filter bots, slice top 8, render strip

    Build->>CP: render /contributors page
    CP->>GH: GET /repos/multivmlabs/aeo.js/contributors
    GH-->>CP: JSON (or error → fetchError=true)
    CP->>CP: filter bots, sort, render grid or fallback

    Note over HP,CP: Two independent API calls per build — share a helper to consume rate limit only once
Loading
Prompt To Fix All With AI
Fix the following 3 code review issues. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 3
website/src/components/HomepageContributors.astro:13-25
**Duplicate GitHub API call doubles rate-limit consumption**

Both `HomepageContributors.astro` and `contributors.astro` independently call the same GitHub API endpoint (`/repos/multivmlabs/aeo.js/contributors`) at build time. Every build fires 2 unauthenticated requests against the 60 req/hr limit. The PR description already flags rate-limit risk and recommends adding a `GITHUB_TOKEN` — having two separate fetches makes that problem arrive twice as fast.

Consider extracting the fetch into a shared Astro content helper (e.g. `src/lib/contributors.ts`) so both components consume the same in-memory result during a single build.

### Issue 2 of 3
website/src/components/HomepageContributors.astro:1-8
**`GhContributor` type duplicated across both components**

The same `GhContributor` type is defined identically in both `HomepageContributors.astro` and `contributors.astro`. Moving it to a shared file (e.g. `src/lib/contributors.ts`) alongside the shared fetch would eliminate this duplication and make future field additions a single-place change.

### Issue 3 of 3
website/src/components/HomepageContributors.astro:44
**Fragile `avatar_url` query-string concatenation**

`${c.avatar_url}&s=80` assumes the URL already contains a `?` query parameter. GitHub's current format (`https://avatars.githubusercontent.com/u/12345?v=4`) always does, but a safer approach is to construct the URL properly:

```ts
const avatarSrc = new URL(c.avatar_url);
avatarSrc.searchParams.set('s', '80');
```

Same pattern applies to the `&s=120` append in `contributors.astro:58`.

Reviews (2): Last reviewed commit: "feat(website): contributors strip on hom..." | Re-trigger Greptile

Comment on lines +98 to +130
text-decoration: underline;
text-underline-offset: 0.2em;
}
.contrib-grid {
list-style: none;
padding: 0;
margin: 2rem 0;
display: grid;
grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
gap: 0.75rem;
}
.contrib-card {
margin: 0;
}
.contrib-link {
display: flex;
flex-direction: column;
align-items: center;
gap: 0.5rem;
padding: 1rem 0.75rem;
border-radius: 12px;
background: rgba(255, 255, 255, 0.03);
border: 1px solid rgba(255, 255, 255, 0.08);
color: inherit;
text-decoration: none;
transition: border-color 0.15s ease, background 0.15s ease, transform 0.15s ease;
}
.contrib-link:hover {
border-color: rgba(255, 255, 255, 0.2);
background: rgba(255, 255, 255, 0.06);
transform: translateY(-1px);
}
.contrib-avatar {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Hardcoded dark-mode colors break light theme

All color values are hardcoded in dark-mode terms (#fff, rgba(255, 255, 255, 0.6), rgba(255, 255, 255, 0.03/0.08/0.2/0.06)). Starlight ships with a built-in light/dark theme toggle; on the light theme, white text on an off-white card background would be nearly invisible. Prefer CSS custom properties from Starlight's design tokens (e.g. var(--sl-color-text), var(--sl-color-bg-sidebar)) so the cards adapt automatically.

Prompt To Fix With AI
This is a comment left during a code review.
Path: website/src/pages/contributors.astro
Line: 98-130

Comment:
**Hardcoded dark-mode colors break light theme**

All color values are hardcoded in dark-mode terms (`#fff`, `rgba(255, 255, 255, 0.6)`, `rgba(255, 255, 255, 0.03/0.08/0.2/0.06)`). Starlight ships with a built-in light/dark theme toggle; on the light theme, white text on an off-white card background would be nearly invisible. Prefer CSS custom properties from Starlight's design tokens (e.g. `var(--sl-color-text)`, `var(--sl-color-bg-sidebar)`) so the cards adapt automatically.

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines +52 to +55
</p>
<ul class="contrib-grid">
{contributors.map((c) => (
<li class="contrib-card">
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 rel="noopener" should also include noreferrer

All target="_blank" anchors use only rel="noopener". Adding noreferrer is the recommended practice — it also suppresses the Referer header and independently implies noopener in all browsers, so it's strictly safer without any downside. The same applies to the contributor card links (href={c.html_url}) and the CTA link.

Suggested change
</p>
<ul class="contrib-grid">
{contributors.map((c) => (
<li class="contrib-card">
<a href="https://github.com/multivmlabs/aeo.js/graphs/contributors" target="_blank" rel="noopener noreferrer">
Prompt To Fix With AI
This is a comment left during a code review.
Path: website/src/pages/contributors.astro
Line: 52-55

Comment:
**`rel="noopener"` should also include `noreferrer`**

All `target="_blank"` anchors use only `rel="noopener"`. Adding `noreferrer` is the recommended practice — it also suppresses the `Referer` header and independently implies `noopener` in all browsers, so it's strictly safer without any downside. The same applies to the contributor card links (`href={c.html_url}`) and the CTA link.

```suggestion
      <a href="https://github.com/multivmlabs/aeo.js/graphs/contributors" target="_blank" rel="noopener noreferrer">
```

How can I resolve this? If you propose a fix, please make it concise.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: cc8ed64bca

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".


try {
const res = await fetch(
'https://api.github.com/repos/multivmlabs/aeo.js/contributors?per_page=100',
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Paginate GitHub contributors API results

The contributors page fetches only a single page (per_page=100) and never follows pagination links, so once the repository exceeds 100 human contributors this page will silently omit everyone after the first page and present an incomplete list. Because this route is meant to represent project contributors, the data becomes inaccurate in normal growth scenarios unless additional pages are fetched.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown

@claude claude Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good — docs-only changes (heavy GIFs swapped for tiny WebPs, plus a build-time-static contributors page with graceful fallback).

Extended reasoning...

Overview

This PR makes two website-only changes: (1) replaces 4 animated GIFs (~4.5 MB) referenced in introduction.mdx and widget.mdx with WebP still frames (~30 KB total), and (2) adds a new /contributors Astro page that fetches the GitHub contributors API at build time, filters bots via type === 'User', sorts by contribution count, and renders a static card grid. A nav link is also added in Header.astro between "Docs" and "Checker".

Security risks

None of significance. The new page issues an unauthenticated GET to api.github.com/repos/multivmlabs/aeo.js/contributors at build time only — no secrets, no user input, no runtime fetch. Output is rendered through Astro's default-escaped JSX ({c.login}, {c.contributions}), and href values come straight from the GitHub API response. External links use target="_blank" rel="noopener". On fetch failure (rate limit, network), the page falls back to a static GitHub link rather than crashing the build.

Level of scrutiny

Low. This is a documentation site under website/ with no impact on the published aeo.js package or any production logic. The asset swap is mechanical, and the contributors page is self-contained with a graceful fallback path.

Other factors

The bug hunting system flagged a nit on contributors.astro (subject-verb disagreement when length === 1, plus an empty-success edge case where a 202 [] response from GitHub would render "0 contributors have shaped aeo.js" with an empty grid). Both are user-facing copy/UX polish on a docs page rather than functional bugs — the existing fallback already handles 4xx/5xx, and the page builds and renders correctly for the current 4-contributor list. Worth a one-line tweak but not blocking.

Comment on lines +50 to +52
<p class="contrib-intro">
{contributors.length} contributor{contributors.length === 1 ? '' : 's'} have shaped aeo.js. Thank you. 🙏
</p>
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Two edge-case rendering issues in the success branch's intro text at website/src/pages/contributors.astro lines 50–52: (1) subject-verb disagreementcontributor{...=== 1 ? '' : 's'} pluralizes the noun but "have" is hardcoded, so a single contributor renders "1 contributor have shaped aeo.js." (should be "has shaped"). (2) Empty-success state — when res.ok is true but the contributor list is empty (e.g. GitHub's /contributors endpoint returns 202 with [] while stats are recomputing, or the type === 'User' filter removes everyone), fetchError stays false and the page renders "0 contributors have shaped aeo.js. Thank you. 🙏" plus an empty grid and a "Want to join them?" CTA instead of the graceful GitHub-link fallback. Trivial fix: branch the verb the same way the noun is, and treat !fetchError && contributors.length === 0 like fetchError.

Extended reasoning...

1. Subject-verb disagreement (line 51)

The success branch renders:

{contributors.length} contributor{contributors.length === 1 ? '' : 's'} have shaped aeo.js. Thank you. 🙏

The noun is conditionally pluralized via the ternary, but the verb have is hardcoded. Step-by-step proof for contributors.length === 1:

  1. {contributors.length}1
  2. contributor literal → contributor
  3. {contributors.length === 1 ? '' : 's'}'' (empty)
  4. have shaped aeo.js. Thank you. 🙏 literal → unchanged

Concatenated output: "1 contributor have shaped aeo.js. Thank you. 🙏" — a real subject-verb agreement error in user-facing copy. Counts of 0 ("0 contributors have") and 2+ render correctly. The fix mirrors the existing pluralization:

{contributors.length} contributor{contributors.length === 1 ? '' : 's'} {contributors.length === 1 ? 'has' : 'have'} shaped aeo.js.

For multivmlabs/aeo.js this only triggers if the User-filtered count drops to exactly 1 (currently 4), so the blast radius is small — hence nit.

2. Empty-success state (lines 17–28, 50–82)

fetchError is only set when !res.ok or the try throws. Step-by-step proof for the 202-empty-array case:

  1. fetch(...) resolves with res.status === 202. The Response ok getter is true for any status in 200–299, so res.ok === true and fetchError stays false.
  2. await res.json() parses an empty array body [] without throwing (this is the documented behavior of GET /repos/{owner}/{repo}/contributors while GitHub recomputes stats — see GitHub REST API docs).
  3. data.filter(c => c.type === 'User').sort(...) produces [].
  4. contributors.length === 0 and fetchError === false, so the success branch renders.
  5. Output: "0 contributors have shaped aeo.js. Thank you. 🙏", an empty <ul class="contrib-grid">, and "Want to join them? Open a PR." — visually broken even though no error occurred.

The same path is reachable if every entry is filtered out by c.type === 'User' (e.g. only bots committed since the last cache).

Addressing the refutation: the refuting verifier correctly notes the build doesn't crash, the trigger is brief/rare for an established repo, and the next deploy fixes it. That's why this is filed as nit, not normal — the page still renders and the fetchError fallback covers the meaningful 4xx/5xx cases. But the fix is one line and the file is being added in this PR, so it's worth catching now rather than after a confusing user report. The fix is to widen the fallback condition:

{fetchError || contributors.length === 0 ? (
  <p class="contrib-fallback">…GitHub fallback…</p>
) : (
  …existing success markup…
)}

Severity: nit — both issues are low-impact copy/UX polish on a docs page, not functional bugs. They co-locate in the same intro line, so a single edit addresses both.

Adds a small "Built by" avatar strip near the bottom of the homepage
that links to the existing /contributors page. Surfaces the community
on the most-visited entry point without duplicating the full grid.

- New component: website/src/components/HomepageContributors.astro
- Reuses the same build-time GitHub API fetch + bot filter as
  /contributors. On fetch failure, the section silently doesn't render
  (homepage stays clean).
- Top 8 contributors are shown as 40px avatars; if there are more,
  a "+N" badge links to the full page.
- "View all N contributors →" link below the strip.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@rubenmarcus rubenmarcus merged commit 127ac6b into main May 4, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant